// spirit.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <map>
#include <boost/spirit.hpp>
#include <boost/spirit/phoenix.hpp>
#include <boost/spirit/actor.hpp>

using namespace std;
using namespace boost::spirit;
using namespace phoenix;

class calc_closure : public boost::spirit::closure<calc_closure, double>
{
public:
	member1 val;
};

class calculator : public grammar<calculator, calc_closure::context_t>
{
public:
	template<typename ScannerT>
	class definition
	{
	public:
		typedef rule<ScannerT, calc_closure::context_t> rule_type;
		rule_type factor, term, exp;

		rule<ScannerT> start_rule;
		const rule<ScannerT>& start() const {return start_rule;}

		definition(const calculator& self)
		{
			factor = real_p[factor.val=arg1]|
				(alpha_p>>*(alnum_p))[factor.val=self.m_fun_get_value(arg1, arg2)]|
				('('>>exp[factor.val=arg1]>>')');

			term = factor[term.val=arg1]>>
				*( ('*'>>factor[term.val*=arg1]) | ('/'>>factor[term.val/=arg1]) );

			exp = term[exp.val=arg1]>>
				*( ('+'>>term[exp.val+=arg1]) | ('-'>>term[exp.val-=arg1]) );

			start_rule = exp[self.val=arg1];
		}
	};

	calculator(map<string, double>& value_map)
		: m_fun_get_value(get_value_fun(value_map))
	{
	}

	class get_value_fun
	{
	public:
		template<typename P1, typename P2>
		struct result
		{
			typedef double type;
		};

		template<typename P1, typename P2>
		double operator()(const P1& start, const P2& end)
		{
			return m_value_map[string(start, end)];
		}

		get_value_fun(map<string, double>& value_map)
			: m_value_map(value_map)
		{
		}

		map<string, double>& m_value_map;
	};

	const phoenix::function<get_value_fun> m_fun_get_value;
};

int _tmain(int argc, _TCHAR* argv[])
{
	double result;
	string value_name;
	map<string, double> value_map;

	calculator calc(value_map);

	rule<phrase_scanner_t> rule_equal = 
		(
			(alpha_p>>*(alnum_p))[assign(value_name)]>>'='>>calc[assign_a(result)]
		)[insert_at_a(value_map, value_name, result)];

	const char *szEqus[3] = { 
			"PI = 3.1415926", 
			"Rad = PI*2.0/3.0", 
			"Deg = Rad*180/PI"};

	for(int i=0; i<3; i++)
	{
		parse(szEqus[i], rule_equal, space_p);
	}

	return 0;
}

